package homework

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
)

var errInvalidEntity = errors.New("invalid entity")

type Stmt struct {
	uniq map[string]bool
	cnt  int
}

func NewStmt() *Stmt {
	return &Stmt{
		uniq: make(map[string]bool),
		cnt:  0,
	}
}

func (s *Stmt) InsertStmt(entity interface{}) (string, []interface{}, error) {

	field, args, err := s.insertStmt(entity)
	if err != nil {
		return "", nil, err
	}

	typ := reflect.TypeOf(entity)
	for typ.Kind() == reflect.Pointer {
		typ = typ.Elem()
	}
	table := typ.Name()

	// sql 头
	bdHead := strings.Builder{}
	// sql 字段内容
	bdField := strings.Builder{}
	// sql ? VALUE内容
	bdValue := strings.Builder{}
	bd := strings.Builder{}

	bdHead.WriteString("INSERT INTO `")
	bdHead.WriteString(table)

	bdField.WriteString("`(")
	bdValue.WriteString("(")
	for i, v := range field {
		bdField.WriteString("`")
		bdField.WriteString(v)
		if i == len(field)-1 {
			bdValue.WriteString("?")
			bdField.WriteString("`")
		} else {
			bdValue.WriteString("?,")
			bdField.WriteString("`,")
		}

	}
	bdValue.WriteString(")")
	bdField.WriteString(")")

	bd.WriteString(bdHead.String())
	bd.WriteString(bdField.String())
	bd.WriteString(" VALUES")
	bd.WriteString(bdValue.String())
	bd.WriteString(";")
	return bd.String(), args, nil
}

func (s *Stmt) insertStmt(entity interface{}) ([]string, []interface{}, error) {
	if entity == nil {
		return nil, nil, errInvalidEntity
	}

	typ := reflect.TypeOf(entity)
	refVal := reflect.ValueOf(entity)

	num := 0
	for typ.Kind() == reflect.Pointer {
		typ = typ.Elem()
		refVal = refVal.Elem()
		num++
	}

	if num > 1 || typ.Kind() != reflect.Struct {
		return nil, nil, errInvalidEntity
	}

	// 结构体去重
	if _, ok := s.uniq[typ.Name()]; ok {
		return nil, nil, nil
	}
	s.uniq[typ.Name()] = true

	numFiled := typ.NumField()
	if numFiled == 0 {
		return nil, nil, errInvalidEntity
	}

	var field []string
	var args []interface{}

	if refVal.IsValid() {
		fmt.Println()
	}

	// cnt == 0 为一级空结构体
	if !s.isValidLoop(refVal.Interface()) && s.cnt != 0 {
		for i := 0; i < numFiled; i++ {
			fdType := typ.Field(i).Name
			fdVal := refVal.Field(i)
			field = append(field, fdType)
			args = append(args, fdVal.Interface())
		}
		fmt.Println(args)
		return field, args, nil
	}

	for i := 0; i < numFiled; i++ {
		fdType := typ.Field(i).Name
		fdVal := refVal.Field(i)

		if fdVal.Kind() == reflect.Struct && fdVal.Type().Name() != "NullString" {

			refField, refArgs, refErr := s.insertStmt(fdVal.Interface())
			if refErr != nil {
				return nil, nil, refErr
			}
			field = append(field, refField...)
			args = append(args, refArgs...)
			continue
		}

		args = append(args, fdVal.Interface())
		field = append(field, fdType)

	}
	return field, args, nil
}

func (s *Stmt) isValidLoop(entity interface{}) bool {

	refVal := reflect.ValueOf(entity)
	numFiled := refVal.NumField()

	for i := 0; i < numFiled; i++ {
		fdVal := refVal.Field(i)
		switch fdVal.Kind() {
		case reflect.Pointer:
			if !fdVal.IsNil() {
				return true
			}
		case reflect.Int64, reflect.Int32:
			if !fdVal.IsZero() {
				return true
			}
		case reflect.Bool:
			if !fdVal.IsZero() {
				return true
			}
		case reflect.Struct:
			s.cnt++
			if s.isValidLoop(fdVal.Interface()) {
				return true
			}
		}
	}
	return false
}
